/**
 * @file drv_usart.c
 * @brief 
 * 
 * @author dalin (dalin@robot.com)
 * @version 1.0
 * @date 2023-05-08
 * 
 * @copyright Copyright (c) 2023  OpenSir Tech.co, Ltd
 * 
 * @par 修改日志:
 * <table>
 * <tr><th>Date       <th>Version <th>Author  <th>Description
 * <tr><td>2023-05-08 <td>1.0     <td>dalin     <td>INIT
 * <tr><td>2023-05-15 <td>1.1     <td>dalin     <td>添加阻塞，中断，DMA三种模式
 * <tr><td>2023-05-16 <td>1.2     <td>dalin     <td>添加不定长接收
 * </table>
 */
#include "cmsis_os.h"
#include "drv_usart.h"

#ifdef USE_USART_DRIVER
#include "usart.h"
#include "dma.h"
#include "mlog.h"



__weak UART_HandleTypeDef huart1;
__weak UART_HandleTypeDef huart2;
__weak UART_HandleTypeDef huart3;
__weak UART_HandleTypeDef huart4;
__weak UART_HandleTypeDef huart5;
__weak UART_HandleTypeDef huart6;


__weak DMA_HandleTypeDef hdma_usart1_rx;
__weak DMA_HandleTypeDef hdma_usart1_tx;
__weak DMA_HandleTypeDef hdma_usart2_rx;
__weak DMA_HandleTypeDef hdma_usart2_tx;
__weak DMA_HandleTypeDef hdma_usart3_rx;
__weak DMA_HandleTypeDef hdma_usart3_tx;
__weak DMA_HandleTypeDef hdma_uart4_rx;
__weak DMA_HandleTypeDef hdma_uart4_tx;
__weak DMA_HandleTypeDef hdma_uart5_rx;
__weak DMA_HandleTypeDef hdma_uart5_tx;
__weak DMA_HandleTypeDef hdma_usart6_rx;
__weak DMA_HandleTypeDef hdma_usart6_tx;


/* 打印函数 */
PUTCHAR_PROTOTYPE
{
//	HAL_UART_Transmit_DMA(PRINTF_API,(uint8_t *)&ch,1);
  HAL_UART_Transmit(PRINTF_API,(uint8_t *)&ch,1,0xFFFF);
  return ch;
}


/*
 * 串口传输收发的三种方式：
 * 1、阻塞收发
 * 2、中断收发
 * 3、DMA收发 ：这里实现
 * 
 * 串口发送/接收函数：
 * HAL_UART_Transmit();             串口发送数据，使用超时管理机制
 * HAL_UART_Receive();              串口接收数据，使用超时管理机制
 *  
 * HAL_UART_Transmit_IT();          串口中断模式发送
 * HAL_UART_Receive_IT();           串口中断模式接收
 *  
 * HAL_UART_Transmit_DMA();         串口DMA模式发送
 * HAL_UART_Transmit_DMA();         串口DMA模式接收
 *  
 * HAL_UART_DMAPause();             暂停串口DMA
 * HAL_UART_DMAResume();            恢复串口DMA
 * HAL_UART_DMAStop();              结束串口DMA
 *  
 * HAL_UART_IRQHandler();           串口中断处理函数
 * HAL_UART_TxCpltCallback();       串口发送中断回调函数
 * HAL_UART_TxHalfCpltCallback();   串口发送一半中断回调函数（用的较少）
 * HAL_UART_RxCpltCallback();       串口接收中断回调函数
 * HAL_UART_RxHalfCpltCallback();   串口接收一半回调函数（用的较少）
 * HAL_UART_ErrorCallback();        串口接收错误函数
 */

static UART_HandleTypeDef * get_usart_handle(usartNumber_e usart)
{
    UART_HandleTypeDef *handle = NULL;
    
    /* 映射TIM */
    switch (usart)
    {
    case DRV_UART1:
        handle = &huart1;
        break;
    case DRV_UART2:
        handle = &huart2;
        break;
    case DRV_UART3:
        handle = &huart3;
        break;
    case DRV_UART4:
        handle = &huart4;
        break;
    case DRV_UART5:
        handle = &huart5;
        break;
    case DRV_UART6:
        handle = &huart6;
        break;
    
    default:
        log_error("usart %d error", usart);

        break;
    }
    return handle;
}

/*************************************** 阻塞 ***************************************/

/**
 * @brief 阻塞发送
 * 
 * @param  uart             : usart ID 
 * @param  data             : 数据地址
 * @param  len              : 数据长度
 * @param  timeout          : 阻塞时间
 * 
 * @return int 
 */
int drv_usart_send(usartNumber_e usart, uint8_t * data, uint16_t len, uint32_t timeout)
{
    UART_HandleTypeDef  *handle;

    /* 映射usart头 */
    handle = get_usart_handle(usart);
    if(handle == NULL)
    {
        return DRV_ERROR;
    }

    HAL_UART_Transmit(handle, data, len, timeout);

    return DRV_EOK;
}


int  drv_usart_recv(usartNumber_e usart, uint8_t * data, uint16_t len, uint32_t timeout)
{
    UART_HandleTypeDef  *handle;

    /* 映射usart头 */
    handle = get_usart_handle(usart);
    if(handle == NULL)
    {
        return DRV_ERROR;
    }

    HAL_UART_Receive(handle, data, len, timeout);

    return DRV_EOK;    
}

/*************************************** 中断 ***************************************/

/**
 * @brief 中断发送（需要将该接口放在对应的中断服务函数中）
 * 
 * @param  usart            : xxx
 * @param  data             : xxx
 * @param  len              : xxx
 * 
 */
int drv_usart_send_it(UART_HandleTypeDef *usart, uint8_t *data, uint16_t len)
{

    HAL_UART_Transmit_IT(usart, data, len);

    return DRV_EOK;
}

/**
 * @brief 中断接收（需要将该接口放在对应的中断服务函数中）
 * 
 * @param  usart            : xxx
 * @param  data             : xxx
 * @param  len              : xxx
 * 
 */
int drv_usart_recv_it(UART_HandleTypeDef *usart, uint8_t * data, uint16_t len)
{

    HAL_UART_Receive_IT(usart, data, len);

    return DRV_EOK;    
}


/**
 * @brief 使能中断接收
 * 
 * @param  usart            : usart ID
 * @param  data             : 接收缓存
 * @param  len              : 接收长度
 * 
 */
int drv_usart_recv_it_enable(usartNumber_e usart, uint8_t * data, uint16_t len)
{
    UART_HandleTypeDef  *handle;

    /* 映射usart头 */
    handle = get_usart_handle(usart);
    if(handle == NULL)
    {
        return DRV_ERROR;
    }

    HAL_UART_Receive_IT(handle, data , len);
	return DRV_EOK;  
}


void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
	if(huart->Instance == USART1)
        huart->gState = HAL_UART_STATE_READY;//huart1传输完成 变为准备	
}

/* 接收回调函数 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{

}

/*************************************** DMA ***************************************/

/**
 * @brief 如果使用DMA，获取对应的发送dma通道
 * 
 * @param  usart            : 串口 ID
 * 
 * @return DMA_HandleTypeDef*  
 */
static DMA_HandleTypeDef * get_usart_dma_tx_ch(usartNumber_e uart)
{
    DMA_HandleTypeDef *handle = NULL;
    
    /* 映射TIM */
    switch (uart)
    {
    case DRV_UART1:
        handle = &hdma_usart1_tx;
        break;
    case DRV_UART2:
        handle = &hdma_usart2_tx;
        break;
    case DRV_UART3:
        handle = &hdma_usart3_tx;
        break;
    case DRV_UART4:
        handle = &hdma_uart4_tx;
        break;
    case DRV_UART5:
        handle = &hdma_uart5_tx;
        break;
    case DRV_UART6:
        handle = &hdma_usart6_tx;
        break;

    default:
        log_error("usart tx dma %d error", uart);

        break;
    }
    return handle;
}
/**
 * @brief 如果使用DMA，获取对应的接送dma通道
 * 
 * @param  usart            : 串口 ID
 * 
 * @return DMA_HandleTypeDef*  
 */
static DMA_HandleTypeDef * get_usart_dma_rx_ch(usartNumber_e uart)
{
    DMA_HandleTypeDef *handle = NULL;
    
    /* 映射TIM */
    switch (uart)
    {
    case DRV_UART1:
        handle = &hdma_usart1_rx;
        break;
    case DRV_UART2:
        handle = &hdma_usart2_rx;
        break;
    case DRV_UART3:
        handle = &hdma_usart3_rx;
        break;
    case DRV_UART4:
        handle = &hdma_uart4_rx;
        break;
    case DRV_UART5:
        handle = &hdma_uart5_rx;
        break;
    case DRV_UART6:
        handle = &hdma_usart6_rx;
        break;

    default:
        log_error("usart rx dma %d error", uart);

        break;
    }
    return handle;
}


/**
 * @brief dma发送数据
 * 
 * @param  uart             : usart ID 
 * @param  data             : 数据地址
 * @param  len              : 数据长度
 * 
 * @return int 
 */
int drv_uart_dma_send(usartNumber_e uart, uint8_t *data, uint16_t len)
{
    UART_HandleTypeDef  *handle;
    DMA_HandleTypeDef   *dma_ch;

    /* 映射usart头 */
    handle = get_usart_handle(uart);
    if(handle == NULL)
    {
        return DRV_ERROR;
    }
    /* 映射dma通道 */
    dma_ch = get_usart_dma_tx_ch(uart);
    if(handle == NULL)
    {
        return DRV_ERROR;
    }
 
    /* 等待上一次的数据发送完毕 */
	while(HAL_DMA_GetState(dma_ch) != HAL_DMA_STATE_READY) osDelay(2);

    /* 判断是否发送正常，如果出现异常则进入异常中断函数 */
    if(HAL_UART_Transmit_DMA(handle, data, len)!= HAL_OK) 
    {
        Error_Handler();
    }    

    return DRV_EOK;
}


/**
 * @brief dma接收数据数据
 * 
 * @param  uart             : usart ID 
 * @param  data             : 数据地址
 * @param  len              : 数据长度
 * 
 * @return int 
 */
int drv_uart_dma_recv(usartNumber_e uart,uint8_t *data, uint16_t len)
{
    UART_HandleTypeDef  *handle;

    /* 映射usart头 */
    handle = get_usart_handle(uart);
    if(handle == NULL)
    {
        return DRV_ERROR;
    }

    /* 重新打开DMA接收 */
    HAL_UART_Receive_DMA(handle, data, len);

    return DRV_EOK;
}


/*************************************** DMA+IDLE：不定长接收 ***************************************/
usartDmaIdle_t dam_rx;

/**
 * @brief 启动空闲中断接收数据
 * 
 * @param  uart             : xxx
 * @param  data             : xxx
 * @param  len              : xxx
 * 
 */
int uart_start_idle_recv(usartNumber_e uart, uint8_t * data)
{
    UART_HandleTypeDef  *handle;

    /* 映射usart头 */
    handle = get_usart_handle(uart);
    if(handle == NULL)
    {
        return DRV_ERROR;
    }

	HAL_UART_Receive_DMA(handle, data, DMA_IDLE_LEN);
	__HAL_UART_ENABLE_IT(handle, UART_IT_IDLE);    
}

/**
 * @brief 利用空闲中断接收usart数据（需要放到中断服务函数中进行调用）
 * 使用该函数前提条件：uart_start_idle_recv()
 * 1. 设置DMA接收缓存
 *      HAL_UART_Receive_DMA(&huartx, rx.buf, DMA_IDLE_LEN); 
 * 2. 打开空闲中断
 *      __HAL_UART_ENABLE_IT(&huartx, UART_IT_IDLE);
 * 
 * @param  uart             : usart ID
 * 
 */ 
int drv_uart_idle(usartNumber_e uart)
{
    UART_HandleTypeDef  *handle;
    DMA_HandleTypeDef   *dma_ch;
    uint32_t tmp_flag = 0;
    uint32_t temp;

    /* 映射usart头 */
    handle = get_usart_handle(uart);
    if(handle == NULL)
    {
        return DRV_ERROR;
    }
    /* 映射dma通道 */
    dma_ch = get_usart_dma_rx_ch(uart);
    if(handle == NULL)
    {
        return DRV_ERROR;
    }

    /* 获取IDLE标志位 */
    tmp_flag =__HAL_UART_GET_FLAG(handle,UART_FLAG_IDLE); 

    /* idle标志被置位 */
    if((tmp_flag != RESET))
    { 
        /* 清除标志位 */
        __HAL_UART_CLEAR_IDLEFLAG(handle);
        HAL_UART_DMAStop(handle); 

        /* 获取DMA中未传输的数据个数  */
        temp  =  __HAL_DMA_GET_COUNTER(dma_ch);  
        /* 总计数减去未传输的数据个数，得到已经接收的数据个数 */
        dam_rx.len =  DMA_IDLE_LEN - temp; 

        /* 接受完成标志位置1 */
        dam_rx.flag = 1;		
    }
    return DRV_EOK;
}

/**
 * @brief 获取idle的数据
 * 
 * @param  uart             : usart ID
 * @param  data             : 数据缓冲区
 * 
 * @return uint16_t  > 0: 返回数据的数量  0: 未接受到数据
 */
uint16_t usart_idle_get_data(usartNumber_e uart, uint8_t * data)
{
    UART_HandleTypeDef  *handle;
    uint16_t ret_data = 0;

    /* 映射usart头 */
    handle = get_usart_handle(uart);
    if(handle == NULL)
    {
        return DRV_ERROR;
    }

   if (dam_rx.flag == 1)
   {
        dam_rx.flag = 0;
    
        memcpy(data, dam_rx.buf, dam_rx.len);
        ret_data = dam_rx.len;

        /* 清空缓存 */
        memset(dam_rx.buf, 0 , DMA_IDLE_LEN);
        dam_rx.len = 0;

        /* 重新使能idle中断 */
        uart_start_idle_recv(uart, dam_rx.buf);
   }

    return ret_data;
}

#endif /* USE_USART_DRIVER */
